package com.ngis.model.mdl;

import java.util.ArrayList;

import com.ngis.udxschema.UdxDatasetSchema;
import org.dom4j.Element;



public class ModelBehavior {
	public ModelBehavior()
	{
		mModelDatasetItemList = new ArrayList<ModelDatasetItem>();
		mModelStateList = new ArrayList<ModelState>();
		mModelStateTransitionList = new ArrayList<ModelStateTransition>();
		mProcessParameters = new ArrayList<ModelParameter>();
		mControlParameters = new ArrayList<ModelParameter>();
	}
	
	boolean loadFromXml(Element pModelClassElement) {
		Element behaviorEle = pModelClassElement.element("Behavior");

		Element relatedDatasetsEle = behaviorEle.element("RelatedDatasets");
		Element stateGroupEle = behaviorEle.element("StateGroup");
		Element parameterEle = behaviorEle.element("Parameters");

		if (relatedDatasetsEle == null)
			relatedDatasetsEle = behaviorEle.element("DatasetDeclarations");

		int count = relatedDatasetsEle.elements().size();
		for (int iData = 0; iData < count; iData++) {
			Element childEle = (Element) relatedDatasetsEle.elements().get(iData);
			ModelDatasetItem temp_item = new ModelDatasetItem();
			temp_item.datasetName = childEle.attributeValue("name");
			temp_item.datasetItemDescription = childEle
					.attributeValue("description");
			String temp_type = childEle.attributeValue("type");
			if (temp_type.equals("internal")) {
				temp_item.datasetItemType = EModelDatasetItemType.EMDIT_INTERNAL;
				temp_item.externalId = "";
				// ! TODO
				// parse udx schema, please consider memory control
				UdxDatasetSchema pDataset = new UdxDatasetSchema(null,
						"UdxDeclaration");
				pDataset.LoadFromXmlElement(childEle.elements().get(0));
				temp_item.datasetItem = pDataset;
			} else if (temp_type.equals("external")) {
				temp_item.datasetItemType = EModelDatasetItemType.EMDIT_EXTERNAL;
				temp_item.externalId = childEle.attributeValue("externalId");
				temp_item.datasetItem = null;
			} else if (temp_type.equals("raw")) {
				temp_item.datasetItemType = EModelDatasetItemType.EMDIT_RAW;
				temp_item.externalId = "";
				temp_item.datasetItem = null;
			}
			addModelDatasetItem(temp_item);
		}

		Element states_ele = stateGroupEle.element("States");
		count = states_ele.elements().size();
		for (int iState = 0; iState < count; iState++) {
			Element childEle = (Element) states_ele.elements().get(iState);
			ModelState temp_state = new ModelState();
			temp_state.stateId = childEle.attributeValue("id");
			temp_state.stateName = childEle.attributeValue("name");
			temp_state.stateDecription = childEle.attributeValue("description");
			String temp_type = childEle.attributeValue("type");
			if (temp_type.equals("basic")) {
				temp_state.stateType = EModelStateType.EMST_BASIC;
			} else if (temp_type.equals("group")) {
				temp_state.stateType = EModelStateType.EMST_GROUP;
			}
			// ////////////////////////////////////////////////////////////////////////
			int count1 = childEle.elements().size();
			for (int iEvent = 0; iEvent < count1; iEvent++) {
				Element childEle1 = (Element) childEle.elements().get(iEvent);
				ModelEvent modelEvent = new ModelEvent();
				modelEvent.eventName = childEle1.attributeValue("name");
				modelEvent.eventDescription = childEle1
						.attributeValue("description");
				String eventType = childEle1.attributeValue("type");
				if (eventType.equals("response")) {
					modelEvent.eventType = EModelEventType.EMET_RESPONSE;
				} else if (eventType.equals("noresponse")) {
					modelEvent.eventType = EModelEventType.EMET_NORESPONSE;
				} else if (eventType.equals("control")) {
					modelEvent.eventType = EModelEventType.EMET_CONTROL;
				}
				Element param_ele = (Element) childEle1.elements().get(0);
				if (param_ele.attribute("description") != null)
					modelEvent.parameterDescription = param_ele
							.attributeValue("description");
				else
					modelEvent.parameterDescription = "";
				modelEvent.datasetReference = param_ele
						.attributeValue("datasetReference");
				modelEvent.optional = Boolean.valueOf(childEle1
						.attributeValue("optional"));

				temp_state.modelEvents.add(modelEvent);
			}
			addModelState(temp_state);
		}

		Element state_transition_ele = stateGroupEle.element("StateTransitions");
		count = state_transition_ele.elements().size();
		for (int iTrans = 0; iTrans < count; iTrans++) {
			Element childEle = (Element) state_transition_ele.elements().get(iTrans);
			ModelStateTransition modelStateTransition;
			String fromId = childEle.attributeValue("from");
			String toId = childEle.attributeValue("to");
			addModelStateTransition(fromId, toId);
		}
		
		if (parameterEle != null) {
			Element ppsEle = parameterEle.element("ProcessParameters");
			if (ppsEle != null) {
				for (int index = 0; index < ppsEle.elements().size(); index++) {
					Element ppEle = ppsEle.elements().get(index);
					ModelParameter pp = new ModelParameter(ppEle.attributeValue("key"), ppEle.attributeValue("description"), ppEle.attributeValue("defaultValue"));
					this.mProcessParameters.add(pp);
				}
			}
			
			Element cpsEle = parameterEle.element("ControlParameters");
			if (cpsEle != null) {
				for (int index = 0; index < cpsEle.elements().size(); index++) {
					Element cpEle = cpsEle.elements().get(index);
					ModelParameter cp = new ModelParameter(cpEle.attributeValue("key"), cpEle.attributeValue("description"), cpEle.attributeValue("defaultValue"));
					this.mProcessParameters.add(cp);
				}
			}

		}
		return true;
	}

	boolean formatToXml(Element pModelClassElement) {
		Element behaviorEle = pModelClassElement.addElement("Behavior");

		Element relatedDatasetsEle = behaviorEle.addElement("RelatedDatasets");
		Element stateGroupEle = behaviorEle.addElement("StateGroup");
		Element parametersEle = behaviorEle.addElement("Parameters");

		for (int i = 0; i < mModelDatasetItemList.size(); i++) {
			ModelDatasetItem temp_item = mModelDatasetItemList.get(i);
			Element temp_ele = relatedDatasetsEle.addElement("DatasetItem");
			temp_ele.addAttribute("name", temp_item.datasetName);
			if (temp_item.datasetItemType == EModelDatasetItemType.EMDIT_EXTERNAL) {
				temp_ele.addAttribute("type", "external");
				temp_ele.addAttribute("externalId", temp_item.externalId);
				temp_ele.addAttribute("description",
						temp_item.datasetItemDescription);
			} else if (temp_item.datasetItemType == EModelDatasetItemType.EMDIT_INTERNAL) {
				temp_ele.addAttribute("type", "internal");
				temp_ele.addAttribute("description",
						temp_item.datasetItemDescription);
				if (temp_item.datasetItem != null)
					temp_item.datasetItem.FormatToXmlElement(temp_ele);
			} else if (temp_item.datasetItemType == EModelDatasetItemType.EMDIT_RAW) {
				temp_ele.addAttribute("type", "raw");
				temp_ele.addAttribute("description",
						temp_item.datasetItemDescription);
			}
		}

		Element states_ele = stateGroupEle.addElement("States");
		for (int i = 0; i < mModelStateList.size(); i++) {
			ModelState temp_state = mModelStateList.get(i);
			Element temp_state_ele = states_ele.addElement("State");
			temp_state_ele.addAttribute("id", temp_state.stateId);
			temp_state_ele.addAttribute("name", temp_state.stateName);
			if (temp_state.stateType == EModelStateType.EMST_BASIC)
				temp_state_ele.addAttribute("type", "basic");
			else if (temp_state.stateType == EModelStateType.EMST_GROUP)
				temp_state_ele.addAttribute("type", "group");
			else
				temp_state_ele.addAttribute("type", "unknown");
			temp_state_ele.addAttribute("description",
					temp_state.stateDecription);

			// ////////////////////////////////////////////////////////////////////////
			for (int iEvent = 0; iEvent < temp_state.modelEvents.size(); iEvent++) {
				ModelEvent temp_event = temp_state.modelEvents.get(iEvent);
				EModelEventType eventType = temp_event.eventType;
				Element event_ele = temp_state_ele.addElement("Event");
				event_ele.addAttribute("name", temp_event.eventName);
				if (eventType == EModelEventType.EMET_RESPONSE) {
					event_ele.addAttribute("type", "response");
					Element param_ele = event_ele
							.addElement("ResponseParameter");
					param_ele.addAttribute("datasetReference",
							temp_event.datasetReference);
					param_ele.addAttribute("description",
							temp_event.parameterDescription);
				} else if (eventType == EModelEventType.EMET_NORESPONSE) {
					event_ele.addAttribute("type", "noresponse");
					Element param_ele = event_ele
							.addElement("DispatchParameter");
					param_ele.addAttribute("datasetReference",
							temp_event.datasetReference);
					param_ele.addAttribute("description",
							temp_event.parameterDescription);
				} else if (eventType == EModelEventType.EMET_CONTROL) {
					event_ele.addAttribute("type", "control");
					Element param_ele = event_ele
							.addElement("ControlParameter");
					param_ele.addAttribute("datasetReference",
							temp_event.datasetReference);
					param_ele.addAttribute("description",
							temp_event.parameterDescription);
				}
				event_ele.addAttribute("optional",
						String.valueOf(temp_event.optional));
				event_ele.addAttribute("description",
						temp_event.eventDescription);
			}
		}

		Element state_transition_ele = stateGroupEle
				.addElement("StateTransitions");
		for (int i = 0; i < mModelStateTransitionList.size(); i++) {
			ModelStateTransition it = mModelStateTransitionList.get(i);
			String temp_from = it.fromState.stateId;
			String temp_to = it.toState.stateId;
			Element temp_ele = state_transition_ele.addElement("Add");
			temp_ele.addAttribute("from", temp_from);
			temp_ele.addAttribute("to", temp_to);
		}
		
		Element ppsEle = parametersEle.addElement("ProcessParameters");
		for (int index = 0; index < this.mProcessParameters.size(); index++) {
			Element ppEle = ppsEle.addElement("Add");
			ppEle.addAttribute("key", this.mProcessParameters.get(index).Key);
			ppEle.addAttribute("description", this.mProcessParameters.get(index).Description);
			ppEle.addAttribute("defaultValue", this.mProcessParameters.get(index).DefaultValue);
		}

		Element cpsEle = parametersEle.addElement("ControlParameters");
		for (int index = 0; index < this.mControlParameters.size(); index++) {
			Element cpEle = cpsEle.addElement("Add");
			cpEle.addAttribute("key", this.mControlParameters.get(index).Key);
			cpEle.addAttribute("description", this.mControlParameters.get(index).Description);
			cpEle.addAttribute("defaultValue", this.mControlParameters.get(index).DefaultValue);
		}
		
		return true;
	}

	// ////////////////////////////////////Dataset Declarations////////////////////////////////////
	public boolean addModelDatasetItem(ModelDatasetItem pDataset) {
		for (int i = 0; i < mModelDatasetItemList.size(); i++) {
			if (mModelDatasetItemList.get(i).datasetName.equals(pDataset.datasetName)) {
				return false;
			}
		}
		mModelDatasetItemList.add(pDataset);
		return true;
	}

	public boolean removeModelDatasetItem(ModelDatasetItem pDataset) {
		for (int i = 0; i < mModelDatasetItemList.size(); i++) {
			ModelDatasetItem it = mModelDatasetItemList.get(i);
			if (it.datasetName.equals(pDataset.datasetName)) {
				mModelDatasetItemList.remove(it);
				return true;
			}
		}
		return false;
	}

	public int getModelDatasetItemCount() {
		return mModelDatasetItemList.size();
	}

	public ModelDatasetItem getModelDatasetItem(int idx) {
		if (idx < 0 || idx >= mModelDatasetItemList.size())
			return null;
		ModelDatasetItem pDataset = mModelDatasetItemList.get(idx);
		return pDataset;
	}

	public ModelDatasetItem getModelDatasetItem(String pName) {
		for (int i = 0; i < mModelDatasetItemList.size(); i++) {
			if (mModelDatasetItemList.get(i).datasetName.equals(pName)) {
				ModelDatasetItem pDataset = mModelDatasetItemList.get(i);
				return pDataset;
			}
		}
		return null;
	}

	public boolean updateModelDatasetItem(int idx, ModelDatasetItem pDataset) {
		if (idx < 0 || idx >= mModelDatasetItemList.size())
			return false;
		mModelDatasetItemList.remove(idx);
		mModelDatasetItemList.add(idx, pDataset);
		return true;
	}

	// ////////////////////////////////////Model State
	// Group////////////////////////////////////
	public boolean addModelState(ModelState pState) {
		for (int i = 0; i < mModelStateList.size(); i++) {
			if (mModelStateList.get(i).stateId.equals(pState.stateId)) {
				return false;
			}
		}
		mModelStateList.add(pState);
		return true;
	}

	public boolean removeModelState(ModelState pState) {
		for (int i = 0; i < mModelStateList.size(); i++) {
			ModelState it = mModelStateList.get(i);
			if (it.stateId.equals(pState.stateId)) {
				mModelStateList.remove(it);
				return true;
			}
		}
		return false;
	}

	public int getModelStateCount() {
		return mModelStateList.size();
	}

	public ModelState getModelState(int idx) {
		if (idx < 0 || idx >= mModelStateList.size())
			return null;
		ModelState pState = mModelStateList.get(idx);
		return pState;
	}

	public ModelState getModelState(String pStateId) {
		for (int i = 0; i < mModelStateList.size(); i++) {
			if (mModelStateList.get(i).stateId.equals(pStateId)) {
				return mModelStateList.get(i);
			}
		}
		return null;
	}

	public boolean updateModelState(int idx, ModelState pState) {
		if (idx < 0 || idx >= mModelStateList.size())
			return false;
		mModelStateList.remove(idx);
		mModelStateList.add(idx, pState);
		return true;
	}

	public boolean updateModelState(String pStateId, ModelState pState) {
		for (int i = 0; i < mModelStateList.size(); i++) {
			if (mModelStateList.get(i).stateId.equals(pStateId)) {
				mModelStateList.remove(i);
				mModelStateList.add(i, pState);
				return true;
			}
		}
		return false;
	}

	// ///////////////////////////////////Model State
	// Transition/////////////////////////////////////
	public boolean addModelStateTransition(String pFromStateId,
			String pToStateId) {
		ModelState fromState = getModelState(pFromStateId);
		ModelState toState = getModelState(pToStateId);
		if (fromState!=null && toState!=null) {
			for (int i = 0; i < mModelStateTransitionList.size(); i++) {
				if (mModelStateTransitionList.get(i).fromState.stateId.equals(pFromStateId)
						&& mModelStateTransitionList.get(i).toState.stateId.equals(pToStateId)) {
					return false;
				}
			}
			ModelStateTransition temp_transition = new ModelStateTransition();
			temp_transition.fromState = fromState;
			temp_transition.toState = toState;
			mModelStateTransitionList.add(temp_transition);
			return true;
		}
		return false;
	}

	public boolean addModelStateTransition(ModelState pFromState,
			ModelState pToState) {
		return addModelStateTransition(pFromState.stateId, pToState.stateId);
	}

	public boolean removeModelStateTransition(String pFromStateId,
			String pToStateId) {
		for (int i = 0; i < mModelStateTransitionList.size(); i++) {
			ModelStateTransition it = mModelStateTransitionList.get(i);
			if (it.fromState.stateId.equals(pFromStateId)
					&& it.toState.stateId.equals(pToStateId)) {
				mModelStateTransitionList.remove(it);
				return true;
			}
		}
		return false;
	}

	public boolean removeModelStateTransition(ModelState pFromState,
			ModelState pToState) {
		return removeModelStateTransition(pFromState.stateId, pToState.stateId);
	}

	public int getModelStateTransitionCount() {
		return mModelStateTransitionList.size();
	}

	public ModelStateTransition getModelStateTransition(int idx) {
		if (idx < 0 || idx >= mModelStateTransitionList.size())
			return null;
		ModelStateTransition pStateTransition = mModelStateTransitionList.get(idx);
		return pStateTransition;
	}

	public boolean existModelStatetTransition(String pFromStateId,
			String pToStateId) {
		for (int i = 0; i < mModelStateTransitionList.size(); i++) {
			ModelStateTransition it = mModelStateTransitionList.get(i);
			if (it.fromState.stateId.equals(pFromStateId)
					&& it.toState.stateId.equals(pToStateId)) {
				return true;
			}
		}
		return false;
	}

	public boolean existModelStatetTransition(ModelState pFromState,
			ModelState pToState) {
		return existModelStatetTransition(pFromState.stateId, pToState.stateId);
	}

	public boolean updateModelStateTransition(int idx, String pFromStateId,
			String pToStateId) {
		if (idx < 0 || idx >= mModelStateTransitionList.size())
			return false;
		mModelStateTransitionList.remove(idx);
		addModelStateTransition(pFromStateId, pToStateId);
		return true;
	}

	//////////////////////////////////////////////////////
	
	public boolean addProcessParameter(ModelParameter pp) {
		if (!this.mProcessParameters.contains(pp)) {
			this.mProcessParameters.add(pp);
			return true;
		}
		return false;
	}
	
	public int getProcessParameterCount() {
		return this.mProcessParameters.size();
	}
	
	public ModelParameter getProcessParameter(int index) {
		if (index > (this.mProcessParameters.size())) return null;
		return this.mProcessParameters.get(index);
	}
	
	public boolean removeProcessParameter(int index) {
		if (index > (this.mProcessParameters.size())) return false;
		this.mProcessParameters.remove(index);
		return true;
	}

	
	public boolean addControlParameter(ModelParameter cp) {
		if (!this.mControlParameters.contains(cp)) {
			this.mControlParameters.add(cp);
			return true;
		}
		return false;
	}

	public int getControlParameterCount() {
		return this.mControlParameters.size();
	}
	
	public ModelParameter getControlParameter(int index) {
		if (index > (this.mControlParameters.size())) return null;
		return this.mControlParameters.get(index);
	}
	
	public boolean removeControlParameter(int index) {
		if (index > (this.mControlParameters.size())) return false;
		this.mControlParameters.remove(index);
		return true;
	}
	
	public boolean compareOther(ModelBehavior pBehavior, CompareFlag compareFlag) {
		if (mModelDatasetItemList.size() != pBehavior
				.getModelDatasetItemCount()) {
			compareFlag.Obj = "RelatedDatasets";
			compareFlag.Name = "DatasetItemCount";
			return false;
		}
		for (int i = 0; i < mModelDatasetItemList.size(); i++) {
			ModelDatasetItem temp_item1 = mModelDatasetItemList.get(i);
			ModelDatasetItem temp_item2 = pBehavior.getModelDatasetItem(i);
			if (temp_item1.compareOther(temp_item2) == false) {
				compareFlag.Obj = "DatasetItem";
				compareFlag.Name = temp_item1.datasetName + "<->" + temp_item2.datasetName;
				return false;
			}
		}
		if (mModelStateList.size() != pBehavior.getModelStateCount()) {
			compareFlag.Obj = "States";
			compareFlag.Name = "StateCount";
			return false;
		}
		for (int i = 0; i < mModelStateList.size(); i++) {
			ModelState temp_state1 = mModelStateList.get(i);
			ModelState temp_state2 = pBehavior.getModelState(i);
			if (temp_state1.compareOther(temp_state2) == false) {
				compareFlag.Obj = "State";
				compareFlag.Name = temp_state1.stateName + "<->" + temp_state2.stateName;
				return false;
			}
		}
		if (mModelStateTransitionList.size() != pBehavior.getModelStateTransitionCount()) {
			compareFlag.Obj = "StateTransitions";
			compareFlag.Name = "StateTransitionCount";
			return false;
		}
		for (int i = 0; i < mModelStateTransitionList.size(); i++) {
			ModelStateTransition temp_trans1 = mModelStateTransitionList.get(i);
			ModelStateTransition temp_trans2 = pBehavior.getModelStateTransition(i);
			if (temp_trans1.compareOther(temp_trans2) == false) {
				compareFlag.Obj = "StateTransition";
				compareFlag.Name = temp_trans1.fromState.stateId + "<->"
						+ temp_trans2.fromState.stateId;
				return false;
			}
		}
		return true;
	}

	private ArrayList<ModelDatasetItem> mModelDatasetItemList;
	private ArrayList<ModelState> mModelStateList;
	private ArrayList<ModelStateTransition> mModelStateTransitionList;
	private ArrayList<ModelParameter> mProcessParameters;
	private ArrayList<ModelParameter> mControlParameters;
}
